package com.xique.system.service.biz.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.RandomUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.spatial4j.core.io.GeohashUtils;
import com.xique.common.core.constant.UserConstants;
import com.xique.common.core.domain.R;
import com.xique.common.core.enums.IcbcVendorAuditCodeEnum;
import com.xique.common.core.utils.*;
import com.xique.common.core.web.domain.AjaxResult;
import com.xique.park.api.RemoteParkGarageLaneDeviceService;
import com.xique.park.api.RemoteParkInfoService;
import com.xique.park.api.RemoteParkPayPpService;
import com.xique.system.api.IPayApi;
import com.xique.system.api.dto.SystemProjectDTO;
import com.xique.system.api.request.RemoteSystemProjectAllListRequest;
import com.xique.system.bean.domain.*;
import com.xique.system.bean.dto.AreaDTO;
import com.xique.system.bean.request.*;
import com.xique.system.bean.search.ParkInfoSearch;
import com.xique.system.bean.search.SystemUserSearch;
import com.xique.system.bean.vo.ParkPayIcbcVo;
import com.xique.system.bean.vo.SystemProjectDetailVO;
import com.xique.system.bean.vo.SystemProjectListVO;
import com.xique.system.constants.DictConstant;
import com.xique.system.constants.SystemConstants;
import com.xique.system.service.biz.service.ISystemProjectBizService;
import com.xique.system.service.domain.service.ISystemProjectDomainService;
import com.xique.system.service.service.*;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.geo.Point;
import org.springframework.data.redis.connection.RedisGeoCommands;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author shd
 * @create 2021/12/10 10:42
 */
@Service
public class SystemProjectBizServiceImpl implements ISystemProjectBizService {

    @Autowired
    private IParkInfoService parkInfoService;

    @Autowired
    private ISystemDeptService deptService;

    @Autowired
    private ISystemUserService userService;

    @Autowired
    private ISystemRoleService roleService;

    @Autowired
    private ISystemProjectDomainService projectDomainService;

    @Autowired
    private RemoteParkInfoService remoteParkInfoService;

    @Autowired
    private IAreaService areaService;

    @Autowired
    private RemoteParkPayPpService remoteParkPayPpService;

    @Autowired
    private RemoteParkGarageLaneDeviceService remoteParkGarageLaneDeviceService;

    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    @Autowired
    private IPayApi payApi;

    @Autowired
    private AliossUtil aliossUtil;

    @Autowired
    private ParkPayIcbcService parkPayIcbcService;

    @Override
    public AjaxResult getProjectList(SystemProjectListRequest request) {
        ParkInfoSearch search = new ParkInfoSearch();
        search.setName(request.getName());
        search.setPageNum(request.getPageNum());
        search.setPageSize(request.getPageSize());
        search.setStatus(request.getStatus());
        search.setVerson(request.getVerson());
        search.setIsDoor(request.getIsDoor());
        search.setIsPark(request.getIsPark());
        if (StringUtils.isNotNull(request.getDeptId())) {
            List<SystemDept> children = deptService.selectNormalChildrenDeptById(request.getDeptId());
            List<Long> deptIds = children.stream().map(SystemDept::getDeptId).collect(Collectors.toList());
            deptIds.add(request.getDeptId());
            search.setDeptIds(deptIds);
        }

        IPage<ParkInfo> page = parkInfoService.selectParkInfoPageBySearch(search);

        List<SystemProjectListVO> list = OrikaUtil.converts(page.getRecords(), SystemProjectListVO.class);

        // TODO 所属服务商
        List<Long> deptIds = list.stream().map(SystemProjectListVO::getDeptId).collect(Collectors.toList());
        List<SystemDept> deptList = deptService.selectDeptListByIds(deptIds);
        Map<Long, String> deptNames = deptList.stream().collect(Collectors.toMap(SystemDept::getDeptId, SystemDept::getDeptName));

        List<Long> userIds = list.stream().map(SystemProjectListVO::getUpdateUser).distinct().collect(Collectors.toList());
        List<SystemUser> userList = userService.selectUserListByIds(userIds);
        Map<Long, String> userNames = userList.stream().collect(Collectors.toMap(SystemUser::getUserId, SystemUser::getUserName));

        list.forEach(item -> {
            if (StringUtils.isNotEmpty(deptNames.get(item.getDeptId()))) {
                item.setDeptName(deptNames.get(item.getDeptId()));
            }
            if (StringUtils.isNotEmpty(userNames.get(item.getUpdateUser()))) {
                item.setUpdateBy(userNames.get(item.getUpdateUser()));
            }
        });

        Map<String, Object> map = new HashMap<>(2);
        map.put("list", list);
        map.put("total", page.getTotal());

        return AjaxResult.success(map);
    }

    @Override
    public R<SystemProjectDTO> getProjectInfo(Long parkId) {
        ParkInfoSearch search = new ParkInfoSearch();
        search.setParkId(parkId);

        ParkInfo info = parkInfoService.selectParkInfoBySearch(search);

        SystemProjectDTO dto = OrikaUtil.convert(info, SystemProjectDTO.class);

        return R.ok(dto);
    }

    @Override
    public AjaxResult addProject(SystemProjectAddRequest request) {

        ParkInfoSearch search = new ParkInfoSearch();
        search.setName(request.getName());
        search.setStatus(DictConstant.STATUS_NORMAL);
        search.setRecordStatus(DictConstant.STATUS_NORMAL);
        ParkInfo parkInfoSearch = parkInfoService.selectParkInfoBySearch(search);
        if (StringUtils.isNotNull(parkInfoSearch)) {
            return AjaxResult.error("项目名称重复");
        }

        // 项目信息
        request.setVerson("3");
        ParkInfo parkInfo = OrikaUtil.convert(request, ParkInfo.class);

        // 生成默认账号
        SystemRole roleSearch = new SystemRole();
        roleSearch.setIsDefault(DictConstant.YES);
        roleSearch.setStatus(DictConstant.STATUS_SUCCESS);
        roleSearch.setDelFlag(DictConstant.DEL_FLAG_NORMAL);
        if (StringUtils.isNotBlank(request.getIsDoor()) && SystemConstants.IsDefault.Y.equals(request.getIsDoor())) {
            roleSearch.setRoleKey(SystemConstants.ROLE_KEY_DOOR_MNG);
        }
        if (StringUtils.isNotBlank(request.getIsPark()) && SystemConstants.IsDefault.Y.equals(request.getIsPark())) {
            roleSearch.setRoleKey(SystemConstants.ROLE_KEY_PARKING_MNG);
        }
        if (StringUtils.isNotBlank(request.getIsDoor()) && SystemConstants.IsDefault.Y.equals(request.getIsDoor()) &&
                StringUtils.isNotBlank(request.getIsPark()) && SystemConstants.IsDefault.Y.equals(request.getIsPark())) {
            roleSearch.setRoleKey(SystemConstants.ROLE_KEY_PROJECT_MNG);
        }
        List<SystemRole> roleList = roleService.selectRoleList(roleSearch);
        SystemUser insertUser = null;
        if (CollectionUtils.isNotEmpty(roleList)) {
            SystemRole role = roleList.get(0);
            insertUser = new SystemUser();
            insertUser.setUserType(SystemConstants.UserType.PROJECT);
            insertUser.setDeptId(parkInfo.getDeptId());
            insertUser.setUserName(parkInfo.getName() + "管理员");
            insertUser.setRoleId(role.getRoleId());
            insertUser.setRoleIds(new Long[]{role.getRoleId()});
            // 判断手机号是否已经存在 存在则使用项目名为登录名
            String result = userService.checkLoginNameUnique(parkInfo.getPrincipalPhone());
            if (UserConstants.NOT_UNIQUE.equals(result)) {
                insertUser.setLoginName(parkInfo.getName());
            } else {
                insertUser.setLoginName(parkInfo.getPrincipalPhone());
            }
            insertUser.setPassword(SecurityUtils.encryptPassword("123456"));
        }
        String geoHashCode = getGeoHashCode(parkInfo);
        parkInfo.setGeoCode(geoHashCode);
        boolean isSuccess = projectDomainService.insertProject(parkInfo, insertUser);
        if (!isSuccess) {
            return AjaxResult.error("添加失败");
        }
        Long parkId = parkInfo.getId();
        // 生成车场相关配置
        remoteParkInfoService.initParkInfo(parkId);

        //保存定位信息到Redis
        //saveParkInfoToRedis(insert);
        Map<String, Object> map = new HashMap<>();
        map.put("parkId",parkId);
        return AjaxResult.success(map);
    }

    private String generateVendorId() {
        //先不考虑去重
        String vendorId = RandomUtil.randomNumbers(10);
        return vendorId;
    }

    @Override
    public AjaxResult getProjectDetail(Long id) {

        if (StringUtils.isNull(id)) {
            return AjaxResult.error("项目id不能为空");
        }

        ParkInfo parkInfo = parkInfoService.selectParkInfoById(id);
        if (StringUtils.isNull(parkInfo)) {
            return AjaxResult.error("未找到对应的项目信息");
        }

        SystemDept dept = deptService.selectDeptById(parkInfo.getDeptId());

        AreaDTO areaDTO = areaService.selectAreaNameByCode(parkInfo.getProvince(), parkInfo.getCity(), parkInfo.getDistrict());

        SystemProjectDetailVO vo = OrikaUtil.convert(parkInfo, SystemProjectDetailVO.class);
        vo.setDeptName(dept.getDeptName());
        if (StringUtils.isNotNull(areaDTO)) {
            vo.setProvinceName(areaDTO.getProvinceName());
            vo.setCityName(areaDTO.getCityName());
            vo.setDistrictName(areaDTO.getDistrictName());
        }

        //封装工行支付配置项
        ParkPayIcbc search = new ParkPayIcbc();
        search.setParkId(id);
        List<ParkPayIcbc> parkPayIcbcs = parkPayIcbcService.selectParkPayIcbcList(search);
        if (!parkPayIcbcs.isEmpty()) {
            ParkPayIcbc parkPayIcbc = parkPayIcbcs.get(0);
            vo.setIcbcAuditStatus(parkPayIcbc.getAuditStatus());
        } else {
            vo.setIcbcAuditStatus(IcbcVendorAuditCodeEnum.WAIT_COMMIT.getCode());
        }

        //封装PP支付配置项
        AjaxResult parkPayPp = remoteParkPayPpService.getParkPayPp(id);
        Object data = parkPayPp.get("data");
        if (ObjectUtil.isEmpty(data)){
            //未签约
            vo.setPpAuditStatus("1");
        }else {
            //已签约
            vo.setPpAuditStatus("2");
        }
        return AjaxResult.success(vo);
    }

    @Override
    public AjaxResult editProject(SystemProjectEditRequest request) {

        ParkInfoSearch search = new ParkInfoSearch();
        search.setName(request.getName());
        search.setStatus(DictConstant.STATUS_NORMAL);
        search.setRecordStatus(DictConstant.STATUS_NORMAL);
        List<ParkInfo> list = parkInfoService.selectParkInfoListBySearch(search);
        if (CollectionUtils.isNotEmpty(list) && list.size() > 1) {
            return AjaxResult.error("项目名称已存在");
        }

        ParkInfo update = OrikaUtil.convert(request, ParkInfo.class);
        update.setUpdateUser(SecurityUtils.getUserId());
        update.setUpdateTime(DateUtils.getNowDate());
        String geoHashCode = getGeoHashCode(update);
        update.setGeoCode(geoHashCode);
        boolean isSuccess = parkInfoService.updateById(update);
        if (!isSuccess) {
            return AjaxResult.error("编辑失败");
        }

        //保存定位信息到Redis
//        saveParkInfoToRedis(update);

        return AjaxResult.success("编辑成功");
    }

    @Override
    @Transactional
    public AjaxResult delProject(Long id) {

        if (StringUtils.isNull(id)) {
            return AjaxResult.error("项目id不能为空");
        }

        ParkInfo update = new ParkInfo();
        update.setId(id);
        update.setRecordStatus(DictConstant.STATUS_DELETE);
        boolean isSuccess = parkInfoService.updateById(update);
        if (!isSuccess) {
            return AjaxResult.error("删除失败");
        }

        // 删除项目下所有用户
        SystemUserSearch search = new SystemUserSearch();
        search.setParkId(id);
        List<SystemUser> userList = userService.selectSystemUserListBySearch(search);
        if (CollectionUtil.isEmpty(userList)) {
            return AjaxResult.success("删除成功");
        }
        List<SystemUser> updateList = new ArrayList<>(userList.size());
        userList.forEach(item -> {
            SystemUser updateUser = new SystemUser();
            updateUser.setUserId(item.getUserId());
            updateUser.setDelFlag(UserConstants.DelFlag.DELETE);
            updateList.add(updateUser);
        });
        userService.updateBatchById(updateList);

        //删除支付配置
        AjaxResult result = remoteParkPayPpService.delete(id);
        if (AjaxResult.isError(result)) {
            return AjaxResult.error();
        }

        //删除主相机
        remoteParkGarageLaneDeviceService.deleteDevices(id);

        return AjaxResult.success("删除成功");
    }

    @Override
    public R<List<SystemProjectDTO>> getAllProjectList(RemoteSystemProjectAllListRequest request) {

        ParkInfoSearch search = new ParkInfoSearch();
        search.setIsDoor(request.getIsDoor());
        search.setDistrict(request.getAreaCode());

        List<ParkInfo> list = parkInfoService.selectParkInfoListBySearch(search);

        List<SystemProjectDTO> dtos = OrikaUtil.converts(list, SystemProjectDTO.class);

        return R.ok(dtos);
    }

    @Override
    public AjaxResult getIcbcConfigDetail(Long parkId) {
        ParkPayIcbc search = new ParkPayIcbc();
        search.setParkId(parkId);
        List<ParkPayIcbc> parkPayIcbcs = parkPayIcbcService.selectParkPayIcbcList(search);
        ParkPayIcbc parkPayIcbc = parkPayIcbcs.get(0);
        ParkPayIcbcVo parkPayIcbcVo = OrikaUtil.convert(parkPayIcbc, ParkPayIcbcVo.class);
        return AjaxResult.success(parkPayIcbcVo);
    }

    @Override
    public AjaxResult addParkPayIcbc(IcbcAddVendorRequest request) {
        Long parkId = request.getParkId();
        ParkInfo parkInfo = parkInfoService.selectParkInfoById(parkId);
        parkInfo.setPayChannel(request.getPayChannel());
        parkInfoService.updateById(parkInfo);
        //封装参数
        request.setCertType("101");
        request.setCorprateIdType("0");
        request.setParkId(parkId);
        request.setOutUserId(parkInfo.getOutVendorId());
        request.setOutVendorId(parkInfo.getOutVendorId());
        ParkPayIcbc parkPayIcbc = OrikaUtil.convert(request, ParkPayIcbc.class);
        parkPayIcbc.setAuditStatus(IcbcVendorAuditCodeEnum.IN_AUTDIT.getCode());
        parkPayIcbcService.insertParkPayIcbc(parkPayIcbc);
        // 推送工行
        try {
            payApi.addVendor(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
       return AjaxResult.success();
    }

    @Override
    public AjaxResult updateParkPayIcbc(IcbcUpdateVendorRequest request) {
        ParkPayIcbc parkPayIcbc = OrikaUtil.convert(request, ParkPayIcbc.class);
        parkPayIcbc.setAuditStatus(IcbcVendorAuditCodeEnum.IN_AUTDIT.getCode());
        parkPayIcbcService.updateParkPayIcbc(parkPayIcbc);
        // 推送工行
        try {
            payApi.updateVendor(request);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult uploadPic(ProjectUploadPicRequest request) {
        String ossUrl = aliossUtil.uploadVendorPic(request.getBase64());
        Map<String, String> returnMap = new HashMap<>(3);
        ParkInfo parkInfo = parkInfoService.selectParkInfoById(request.getParkId());
        if (StrUtil.isEmpty(parkInfo.getOutVendorId())){
            parkInfo.setOutVendorId(generateVendorId());
            parkInfoService.updateById(parkInfo);
        }
        request.setOutVendorId(parkInfo.getOutVendorId());
        AjaxResult ajaxResult = payApi.uploadPic(request);
        Map<String,Object> data = (Map<String, Object>) ajaxResult.get("data");
        String icbcPicKey = (String) data.get("imageKey");
        returnMap.put("ossUrl",ossUrl);
        returnMap.put("icbcPicKey",icbcPicKey);
        returnMap.put("outVendorId",parkInfo.getOutVendorId());
        return AjaxResult.success(returnMap);
    }

    public Boolean saveParkInfoToRedis(ParkInfo parkInfo){
        String KEY =  "park_info";
        if (parkInfo.getPositionX() == null || parkInfo.getPositionY() == null
                || parkInfo.getPositionY() > 85.05112878
                || !StrUtil.equals("Y",parkInfo.getIsOpenPark())){
            return false;
        }
        Long flag = redisTemplate.opsForGeo().add(KEY, new RedisGeoCommands.GeoLocation<>(
                parkInfo.getId(),
                new Point(parkInfo.getPositionX(), parkInfo.getPositionY()))
        );
        return flag != null && flag > 0;
    }

    public String getGeoHashCode(ParkInfo parkInfo){
        if (parkInfo.getPositionX() != null && parkInfo.getPositionY() != null && parkInfo.getPositionY() < 85.05112878){
            String geoHashCode = GeohashUtils.encodeLatLon(parkInfo.getPositionY(),parkInfo.getPositionX());
           return geoHashCode;
        }
        return "";
    }
}
